local util = require "util"
local conf = require "conf"
local require = require
local setmetatable = setmetatable
local error = error
local table = table
local ngx = ngx
local os = os
local string = string
local tonumber = tonumber
local pairs = pairs
local message = require 'message'
package.cpath = "/usr/nginx/?.so;;"
local json = require 'cjson'
local gh = require 'iwi'
module(...)
_VERSION = '0.1'

function  create(user, args)
    local db = conf.get_mysql()
    local map = require 'map'
    local decodeURI = util.decodeURI
    local sql = "INSERT INTO `route` SET"
    
    if args.sid then
        sql = sql .. " sid = '" .. args.sid .. "',"
    end

    if args.aid then
        sql = sql .. " aid = " .. args.aid .. ","
    else
        sql = sql .. " aid = " .. user.aid .. ","
    end

    if args.uid then
        sql = sql .. " uid = " .. args.uid .. ","
    else
        sql = sql .. " uid = " .. user.id .. ","
    end

    if args.org_id then
        sql = sql .. " org_id = " .. args.org_id .. ","
    else
        sql = sql .. " org_id = " .. user.org_id .. ","
    end

    if args.name then
        sql = sql .. " name = '" .. args.name .. "',"
    end

    if args.eid then
        sql = sql .. " eid = " .. args.eid .. ","
    end

    if args.height_diff then
        sql = sql .. " height_diff = " .. args.height_diff .. ","
    end
    -- if args.distance then
    --     sql = sql .. " distance = " .. args.distance .. ","
    -- end
    -- if args.duration then
    --     sql = sql .. " duration = " .. args.duration .. ","
    -- end
    if args.detail then
        local detail = decodeURI(args.detail)
        local detailTable = json.decode(detail)
        detail = json.encode(map.setGeoHashesFromRoute(detailTable))
        sql = sql .. " detail = '" .. detail .. "',"
    end

    if args.detail and args.height_diff then
        local duration, distance = util.flightTime(json.decode(args.detail), tonumber(args.height_diff))
        sql = sql .. " distance = " .. distance .. ","
        sql = sql .. " duration = " .. duration .. ","
    end
    sql = sql .. " update_time = " .. os.time() .. ","

    sql = sql .. " create_time = " .. os.time() .. ","

    ngx.log(ngx.ERR, sql)


    local res = db:query(sql:sub(1, -2))
    return res
end

function createController(user, args)
    if args.sid and args.eid then
        if args.sid == args.eid then
            ngx.print(json.encode({
                err = 1,
                msg = '起始站点和结束站点不可一样'
            }))
            ngx.exit(200)
        end
    end
    if args.name then
        local res = search(user, {["exactName"] = args.name})
        if res and #res > 0 then
            ngx.print(json.encode({
                err = 2,
                msg = '名称重复'
            }))
            ngx.exit(200)
        end
    end
    local res = create(user, args)
    local db = conf.get_mysql()

    if not res or res.affected_rows < 1 then
        ngx.print(json.encode({
            err = 1,
            msg = '创建失败'
        }))
        ngx.exit(200)
    else
        local insert_id = res.insert_id
        if insert_id < 1 then
            res = db:query("SELECT LAST_INSERT_ID() AS insert_id")
            if res and res[1] and res[1].insert_id then
                insert_id = res[1].insert_id
            end
        end
        -- ngx.log(ngx.ERR, json.encode(user))
        local routeName = args.name or "未命名"
        local body = user.name .. "于" .. os.date("%H:%M", os.time()) .. "创建了" .. routeName .. "航线"

        message.create(user, {["to_uid"] = 0, ["type"] = "notice", ["header"] = "系统消息", ["body"] = body})
        ngx.print(json.encode({
            err = 0,
            id = insert_id,
            msg = '创建成功'
        }))
        ngx.exit(200)
    end 
end

function generateSubRoutes(user, args)
    local res = search(user, args)
    if not res then
        ngx.print(json.encode({
            err = 1,
            msg = '未找到相应的航路'
        }))
        ngx.exit(200)
    end
    local newArgs = util.deepcopy(res[1])
    local route = json.decode(res[1].detail)

    local subRoutes = util.getSubRoutes(route, 40)
    newArgs.detail = json.encode(subRoutes[1])
    newArgs.name = res[1].name .."←"
    create(user, newArgs)
    newArgs.detail = json.encode(subRoutes[2])
    newArgs.name = res[1].name .."→"
    create(user, newArgs)
    newArgs.detail = json.encode(subRoutes[3])
    newArgs.name = res[1].name .."↖"
    create(user, newArgs)
    newArgs.detail = json.encode(subRoutes[4])
    newArgs.name = res[1].name .."↗"
    create(user, newArgs)
    ngx.print(json.encode({
        err = 0,
        msg = '成功'
    }))
    ngx.exit(200)
end

function generateSubRoutesController(user, args)
    if not args.id then
        ngx.print(json.encode({
            err = 1,
            msg = '缺少id'
        }))
        ngx.exit(200)
    end
    local res = generateSubRoutes(user, args)
end

function update(user, args)
    local map = require 'map'
    local db = conf.get_mysql()
    local decodeURI = util.decodeURI
    local sql = "UPDATE `route` SET "
    if not args.id then
        ngx.print(json.encode({
            err = 2,
            msg = '缺少id'
        }))
    end
    if args.sid then
        sql = sql .. " sid = '" .. args.sid .. "',"
    end

    if args.aid then
        sql = sql .. " aid = " .. args.aid .. ","       
    end

    if args.org_id then
        sql = sql .. " org_id = " .. args.org_id .. ","       
    end

    if args.eid then
        sql = sql .. " eid = " .. args.eid .. ","
    end

    if args.height_diff then
        sql = sql .. " height_diff = " .. args.height_diff .. ","
    end

    if args.detail then
        local detail = decodeURI(args.detail)
        local detailTable = json.decode(detail)
        local index = map.assignMapIndex(detailTable)
        if index and index ~= 0 then
            sql = sql .. " map_index = " .. index .. ","
        end
        detail = json.encode(map.setGeoHashesFromRoute(detailTable))
        sql = sql .. " detail = '" .. detail .. "',"


    end

    if args.name then
        sql = sql .. " name = '" .. args.name .. "',"
    end

    if args.detail and args.height_diff then
        local duration, distance = util.flightTime(json.decode(args.detail), tonumber(args.height_diff))
        sql = sql .. " distance = " .. distance .. ","
        sql = sql .. " duration = " .. duration .. ","
    end
    ngx.log(ngx.ERR, sql)

    sql = sql .. " update_time = " .. os.time() .. ","

    local res = db:query(sql:sub(1, -2) .. " WHERE id = " .. args.id)
    return res
    
end

function updateController(user, args)
    if args.name then
        local res = search(user, {["exactName"] = args.name})
        if res and #res > 0 and  res[1].id ~= args.id then
            ngx.print(json.encode({
                err = 2,
                msg = '名称重复'
            }))
            ngx.exit(200)
        end
    end
    local res = update(user, args)
    if not res then
        ngx.print(json.encode({
            err = 1,
            msg = '更新失败'
        }))
        ngx.exit(200)
    else 
        ngx.print(json.encode({
            err = 0,
            msg = res
        }))
        ngx.exit(200)
    end
end

function delete(user, args)
    local db = conf.get_mysql()

    local res = db:query("UPDATE `route` SET disabled = 1 WHERE id = " .. args.id)
    
    return res
end

function deleteController(user, args)
    if not args.id then
        ngx.print(json.encode({
            err = 2,
            msg = '缺少id'
        }))
    end
    local res = delete(user, args)
    if not res or res.affected_rows < 1 then
        ngx.print(json.encode({
            err = 1,
            msg = '更新失败'
        }))
        ngx.exit(200)
    else 
        ngx.print(json.encode({
            err = 0,
            msg = res
        }))
        ngx.exit(200)
    end
end

function updateDuraAndDis()
    local db = conf.get_mysql()
    local spoint = require 'spoint'
    local routeIds = db:query("SELECT id,detail,sid, eid FROM `route` WHERE disabled = 0")
    for k,v in pairs(routeIds) do
        local vals = {}
        local height_diff = spoint.getHeightDiffBySpointIds(v.sid, v.eid)
        local duration, distance = util.flightTime(json.decode(v.detail), height_diff)
        ngx.log(ngx.ERR, duration, " ",distance)
        ngx.log(ngx.ERR, "routeId"..v.id)
        vals[#vals + 1] = "duration = " .. duration
        vals[#vals + 1] = "distance = " .. distance
        -- vals[#vals + 1] = "update_time = " .. os.time()
        local res, err = db:query("UPDATE `route` set " .. table.concat(vals, ",") .. " WHERE id = " .. v.id)
    end
    ngx.print(json.encode({
        err = 0,
        msg = "ok"
    }))
    ngx.exit(200)
end

function search(user, args)
    local page_no, page_size, id, sname, ename, aid, sid, eid, name = tonumber(args.page_no), tonumber(args.page_size), args.id, args.sname, args.ename, args.aid, args.sid, args.eid, args.name
    local db = conf.get_mysql()
    local sql = "SELECT `route`.id, `route`.aid,`route`.sid , `route`.create_time,`route`.update_time, `route`.name, `route`.detail, `area`.id AS aid, (SELECT name from spoint where id = `route`.sid) AS sname,(SELECT alt from spoint where id = `route`.sid) AS salt, (SELECT alt from spoint where id = `route`.eid) AS ealt, (SELECT name from spoint where id = `route`.eid) AS ename, `route`.eid,`area`.name as area,`route`.`distance`,`route`.duration FROM `route` left join `area` on `route`.aid = `area`.id WHERE `route`.disabled = 0";
    if sname then 
        sql = sql .. " AND (SELECT name from spoint where id = `route`.sid) like '%" .. sname .. "%'"
    end
    if ename then
        sql = sql .. " AND (SELECT name from spoint where id = `route`.eid) like '%" .. ename .. "%'"
    end
    
    if area then
        sql = sql .. " AND area like '%" .. area .. "%'"
    end

    if name then
        sql = sql .. " AND `route`.name like '%" .. name .. "%'"
    end

    if args.exactName then
        sql = sql .. " AND `route`.name = '" .. args.exactName .. "'"
    end

    if args.uid then
        sql = sql .. " AND `route`.uid = " .. user.id
    end

    if id and tonumber(id) then
        sql = sql .. " AND `route`.id = " .. tonumber(id)
    end

    if aid then
        ngx.log(ngx.ERR, json.encode(user))
        local ids = conf.get_aids(tonumber(aid))
        sql = sql .. " AND `area`.id in (" ..  table.concat(ids, ",") .. ")"
    else
        sql = sql .. " AND `area`.id in (" ..  table.concat(user.aids, ",") .. ")"
    end

    if args.org_id then
        local ids = conf.get_orgIds(tonumber(args.org_id))
        sql = sql .. " AND `route`.org_id in (" ..  table.concat(ids, ",") .. ")"
    else
        sql = sql .. " AND `route`.org_id in (" ..  table.concat(user.orgIds, ",") .. ")"
    end

    if sid then
        sql = sql .. " AND `route`.sid = " .. sid
    end
    if eid then
        sql = sql .. " AND `route`.eid = " .. eid
    end

    sql = sql .. " ORDER BY `route`.update_time DESC"
    
    local count = #(db:query(sql) or {})

    if page_no and page_size then
        sql = sql ..  " LIMIT " .. (page_no * page_size) .. "," .. page_size
    end
    ngx.log(ngx.ERR, sql)
    local res = db:query(sql)
    for k,v in pairs(res) do
        v.height_diff = v.ealt - v.salt
    end
    return res, count
end

function searchController(user, args)
    local res, count = search(user, args)
    if not res then
        ngx.print(json.encode({
            err = 1,
            msg = '未找到相应的航路'
        }))
        ngx.exit(200)
    else
        
        ngx.print(json.encode({
            err = 0,
            msg = res,
            count = count
        }))
        ngx.exit(200)
    end
end

function detail(user, args)
    ngx.log(ngx.ERR, json.encode(ngx.fid_weight))
    local res = search(user, args)
    return res
end

function detailController(user, args)
    if not args.id then
        ngx.print(json.encode({
            err = 1,
            msg = '参数不正确'
        }))
        ngx.exit(200)
    end

    local res = detail(user, {["id"] = args.id})

    if not res or not res[1] then
        ngx.print(json.encode({
            err = 1,
            msg = '未找到相应的航路'
        }))
        ngx.exit(200)
    else

        local route = json.decode(res[1].detail)
        local remainBatteryPercentByWeight = {}
        local flightTimeByWeight = {}
        local canFlyTimeByWeight = {}
        for i = 0,5,0.5 do
            local flightTimeDetail = util.flightTimeDetail(route, res[1].height_diff)
            ngx.log(ngx.ERR, json.encode(flightTimeDetail))
            local estimateBatteryAfterFlight = util.estimateBatteryAfterFlight(11.5 + i, flightTimeDetail, util.remainBatteryPercent(50000, false))
            ngx.log(ngx.ERR, estimateBatteryAfterFlight)
            local canFlyTime = util.timeCanFly(11.5, i, flightTimeDetail.total.t)
            ngx.log(ngx.ERR, canFlyTime)
            table.insert(remainBatteryPercentByWeight, {['packweight'] = i, ['battery'] = estimateBatteryAfterFlight})
            table.insert(canFlyTimeByWeight, {['packweight'] = i, ['time'] = canFlyTime})

        end
        ngx.print(json.encode({
            err = 0,
            msg = res,
            remainBatteryPercentByWeight = remainBatteryPercentByWeight,
            canFlyTimeByWeight = canFlyTimeByWeight
        }))
        ngx.exit(200)
    end
end

function getTimeAndDistanceController(user, args)
    if not args.id or not args.lat or not args.lng then
        ngx.print(json.encode({
            err = 1,
            msg = '缺少参数'
        }))
        ngx.exit(200)
    end

    local res = search(user, {["id"] = args.id})
    if not res or #res == 0 then
        ngx.print(json.encode({
            err = 1,
            msg = '未找到相应的航路'
        }))
        ngx.exit(200)
    end
    local t,d = util.flightTimeDistanceGivenP({["lat"] = args.lat, ["lng"] = args.lng}, json.decode(res[1].detail), res[1].height_diff)
     ngx.print(json.encode({
        err = 0,
        t = t,
        d = d
    }))
    ngx.exit(200)
end


function getNextRoutePointController(user, args)
    if (not args.id and not args.route) or not args.lat or not args.lng then
        ngx.print(json.encode({
            err = 1,
            msg = '缺少参数'
        }))
        ngx.exit(200)
    end
    local route
    if args.route then
        route = args.route
    else
        local res = search(user, {["id"] = args.id})
        if not res or #res == 0 then
            ngx.print(json.encode({
                err = 1,
                msg = '未找到相应的航路'
            }))
            ngx.exit(200)
        end
        route = res[1].detail
    end
    
    local index = util.getNextPoint({["lat"] = args.lat, ["lng"] = args.lng}, json.decode(route))
    ngx.print(json.encode({
        err = 0,
        index = index - 1
    }))
end

function default(args)
    if args.action == 'create' then
        local user = conf.parse_token(args, "route")
        createController(user, args)
    elseif args.action == 'update' then
        local user = conf.parse_token(args, "route")
        updateController(user, args)
    elseif args.action == 'search' then
        local user = conf.parse_token(args, "route")
        searchController(user, args)
    elseif args.action == 'delete' then
        local user = conf.parse_token(args, "route")
        deleteController(user, args)
    elseif args.action == 'detail' then
        local user = conf.parse_token(args, "route")
        detailController(user, args)
    elseif args.action == 'getTimeAndDistance' then
        local user = conf.parse_token(args, "route")
        getTimeAndDistanceController(user, args)
    elseif args.action == 'getNextRoutePoint' then
        local user = conf.parse_token(args, "route")
        getNextRoutePointController(user, args)
    elseif args.action == 'generateSubRoutes' then
        local user = conf.parse_token(args, "route")
        generateSubRoutesController(user, args)
    elseif args.action == "updateDuraAndDis" then
        updateDuraAndDis()
    end
end

local class_mt = {
    __newindex = function (table, key, val)
        error('attempt to write to undeclared variable "' .. key .. '"')
    end
}
setmetatable(_M, class_mt)
